<?php
// +----------------------------------------------------------------------
// | 访问官网：https://hawkhawk.club
// | author: hawk
// +----------------------------------------------------------------------
namespace hawk;

use Elasticsearch\ClientBuilder;
use think\facade\Log;

/**
 * MySQL与 es 的参照对比
 * mysql              es
 * 数据库(database)      索引(indices)
 * 表(tables)          类型(types)
 * 行(rows)              文档(documents)
 * 字段(columns)      字段(fields)
 * Class ElasticSearch
 * @package app\common\controller
 */
class ElasticSearch
{
    private $client;

    private $host = [
        '121.4.185.254:9200'
    ];

    const INDEX = 'my_index';

    public function __construct($host=[])
    {
        if($host)$this->host = $host;
        $this->client = ClientBuilder::create()->setHosts($this->host)->build();
    }

    /**
     * 创建索引
     * 相当于数据库中建库建表的操作
     * @param string $index
     * @param array $body
     * @return bool
     */
    public function createIndex(string $index = 'my_index', array $body = []): bool
    {
        $params = [
            'index' => $index, //索引名称
            'body' => [
                'settings' => [ // 设置配置
                    'number_of_shards' => 1, //主分片数
                    'number_of_replicas' => 0 //主分片的副本数
                ],
                'mappings' => [  // 设置映射
                    '_source' => [   // 存储原始文档
                        'enabled' => 'true'
                    ],
                    'properties' => $body // 配置数据结构与类型
                ],
            ]
        ];

        try {
            $this->client->indices()->create($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "创建索引{$index}失败");
            return false;
        }

        return true;
    }

    /**
     * 删除索引（删除数据库）
     * @param string $index
     * @return bool
     */
    public function deleteIndex(string $index = self::INDEX): bool
    {
        $params = [
            'index' => $index
        ];

        try {
            $this->client->indices()->delete($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "删除索引{$index}失败");
            return false;
        }

        return true;
    }

    /**
     * 判断索引是否存在
     * @param string $index
     * @return bool
     */
    public function indexExists(string $index = self::INDEX): bool
    {
        $params = ['index' => $index];

        return $this->client->indices()->exists($params);
    }

    /**
     * 查看映射
     * @param string $index
     * @return array
     */
    public function getMapping(string $index = self::INDEX): array
    {
        $params = [
            'index' => $index,
        ];

        try {
            return $this->client->indices()->getMapping($params);
        } catch (\Exception $e) {
            return json_decode($e->getMessage(), true);
        }
    }

    /**
     * 添加文档（添加一行数据）
     * @param int $id
     * @param array $body
     * @param string $index
     * @return bool
     */
    public function addDos(int $id, array $body, string $index = self::INDEX): bool
    {
        $params = [
            'index' => $index,
            'id' => $id,
            'body' => $body
        ];

        try {
            $this->client->index($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "新增文档{$id}失败");
            return false;
        }

        return true;
    }

    /**
     * 批量插入文档
     * @param array $params
     * @return bool
     */
    public function addAllDos(array $params)
    {
        try {
            $this->client->bulk($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "分批插入失败");
            return false;
        }

        return true;
    }

    /**
     * 获取文档总数（统计）
     * @param string $index
     * @return array|mixed
     */
    public function getCountDos(string $index = self::INDEX)
    {
        $params = [
            'index' => $index,
        ];

        try {
            return $this->client->count($params);
        } catch (\Exception $e) {
            return json_decode($e->getMessage(), true);
        }
    }

    /**
     * 获取文档（获取一行数据）
     * @param int $id
     * @param string $index
     * @return array|mixed
     */
    public function getDoc(int $id, string $index = self::INDEX): array
    {
        $params = [
            'index' => $index,
            'id' => $id
        ];

        try {
            return $this->client->get($params);
        } catch (\Exception $e) {
            return json_decode($e->getMessage(), true);
        }
    }

    /**
     * 更新文档（更新一条数据）
     * @param int $id
     * @param array $body
     * @param string $index
     * @return bool
     */
    public function updateDoc(int $id, array $body, string $index = self::INDEX): bool
    {
        $params = [
            'index' => $index,
            'id' => $id,
            'body' => [
                'doc' => $body
            ]
        ];

        try {
            $this->client->update($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "更新文档{$id}失败");
            return false;
        }

        return true;
    }

    /**
     * 删除文档（删除一条数据）
     * @param int $id
     * @param string $index
     * @return bool
     */
    public function deleteDoc(int $id, string $index = self::INDEX): bool
    {
        $params = [
            'index' => $index,
            'id' => $id
        ];

        try {
            $this->client->delete($params);
        } catch (\Exception $e) {
            Log::write($e->getMessage(), "删除文档{$id}失败");
            return false;
        }

        return true;
    }

    /**
     * 搜索
     * @param string $keywords 检索词
     * @param string $index 索引名
     * @param int $limit 一次显示多少条
     * @param int $page 分页页码，从几开始
     * @return array
     */
    public function searchDoc(string $keywords, string $index = self::INDEX, int $limit = 10, int $page = 1): array
    {
        $offset = ((int)$page - 1) * (int)$limit;
        $params = [
            'index' => $index,
            'body' => [
                '_source' => ['id', 'title'], // 指定返回的字段
                'query' => [
                    // 多字段匹配
//                    'multi_match' => [
//                        'query' => $keywords,
//                        'fields' => ['title', 'content', 'keyword'],
//                        'type' => 'most_fields' // most_fields 多字段匹配度更高   best_fields  完全匹配占比更高
//                    ],
                    // 单个字段匹配
//                    'match' => [
//                        'title' => $keywords
//                    ],
                    // 完全匹配
//                    'match_phrase' => [
//                        'title' => $keywords
//                    ],
                    // 联合查询
                    'bool' => [
                        'should' => [ // 相当于or
                            [
                                'match_phrase' => [
                                    'title' => $keywords
                                ]
                            ],
                            [
                                'match_phrase' => [
                                    'content' => $keywords
                                ]
                            ],
                            [
                                'match_phrase' => [
                                    'keyword' => $keywords
                                ]
                            ],
                        ],
//                        'must' => [ // 相当于and
//                            [
//                                'match' => [
//                                    'title' => $keywords
//                                ]
//                            ],
//                        ],
//                        'should' => [ // 相当于or
//                            [
//                                'match' => [
//                                    'title' => $keywords
//                                ]
//                            ],
//                            [
//                                'match' => [
//                                    'content' => $keywords
//                                ]
//                            ],
//                            [
//                                'match' => [
//                                    'keyword' => $keywords
//                                ]
//                            ],
//                        ],
//                        'must_not' => [ // 相当于not
//                            [
//                                'match' => [
//                                    'content' => $keywords
//                                ]
//                            ]
//                        ],
//                        'filter' => [ // 过滤器  gt 大于；gte 大于等于；lt 小于；lte 小于等于
//                            'range' => [
//                                'id' => ['lt' => 20598, 'gt' => 20590]
//                            ]
//                        ],
                    ],
                ],
                'highlight' => [ // 搜索词高亮设置
//                    'pre_tags' => "<p class='key' style='color: red;'>", // 自定义高亮样式
//                    'post_tags' => "</p>",
                    'fields' => [ // 设置高亮的字段
                        'title' => (object)[],
                        'content' => (object)[],
                        'keyword' => (object)[],
                    ]
                ],
//                'sort' => [['id' => ['order' => 'desc']]], // 排序，不设置会根据最高匹配度来排序
                'from' => $offset, // 分页 - 从哪开始
                'size' => $limit // 一次显示多少条
            ]
        ];

        try {
            return $this->client->search($params);
        } catch (\Exception $e) {
            return json_decode($e->getMessage(), true);
        }
    }
}